"""Support for Overkiz select."""

from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass

from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import OverkizDataConfigEntry
from .const import IGNORED_OVERKIZ_DEVICES
from .entity import OverkizDescriptiveEntity


@dataclass(frozen=True, kw_only=True)
class OverkizSelectDescription(SelectEntityDescription):
    """Class to describe an Overkiz select entity."""

    select_option: Callable[[str, Callable[..., Awaitable[None]]], Awaitable[None]]


def _select_option_open_closed_pedestrian(
    option: str, execute_command: Callable[..., Awaitable[None]]
) -> Awaitable[None]:
    """Change the selected option for Open/Closed/Pedestrian."""
    return execute_command(
        {
            OverkizCommandParam.CLOSED: OverkizCommand.CLOSE,
            OverkizCommandParam.OPEN: OverkizCommand.OPEN,
            OverkizCommandParam.PEDESTRIAN: OverkizCommand.SET_PEDESTRIAN_POSITION,
        }[OverkizCommandParam(option)]
    )


def _select_option_open_closed_partial(
    option: str, execute_command: Callable[..., Awaitable[None]]
) -> Awaitable[None]:
    """Change the selected option for Open/Closed/Partial."""
    return execute_command(
        {
            OverkizCommandParam.CLOSED: OverkizCommand.CLOSE,
            OverkizCommandParam.OPEN: OverkizCommand.OPEN,
            OverkizCommandParam.PARTIAL: OverkizCommand.PARTIAL_POSITION,
        }[OverkizCommandParam(option)]
    )


def _select_option_memorized_simple_volume(
    option: str, execute_command: Callable[..., Awaitable[None]]
) -> Awaitable[None]:
    """Change the selected option for Memorized Simple Volume."""
    return execute_command(OverkizCommand.SET_MEMORIZED_SIMPLE_VOLUME, option)


def _select_option_active_zone(
    option: str, execute_command: Callable[..., Awaitable[None]]
) -> Awaitable[None]:
    """Change the selected option for Active Zone(s)."""
    # Turn alarm off when empty zone is selected
    if option == "":
        return execute_command(OverkizCommand.ALARM_OFF)

    return execute_command(OverkizCommand.ALARM_ZONE_ON, option)


SELECT_DESCRIPTIONS: list[OverkizSelectDescription] = [
    OverkizSelectDescription(
        key=OverkizState.CORE_OPEN_CLOSED_PEDESTRIAN,
        name="Position",
        options=[
            OverkizCommandParam.OPEN,
            OverkizCommandParam.PEDESTRIAN,
            OverkizCommandParam.CLOSED,
        ],
        select_option=_select_option_open_closed_pedestrian,
        translation_key="open_closed_pedestrian",
    ),
    OverkizSelectDescription(
        key=OverkizState.CORE_OPEN_CLOSED_PARTIAL,
        name="Position",
        options=[
            OverkizCommandParam.OPEN,
            OverkizCommandParam.PARTIAL,
            OverkizCommandParam.CLOSED,
        ],
        select_option=_select_option_open_closed_partial,
        translation_key="open_closed_partial",
    ),
    OverkizSelectDescription(
        key=OverkizState.IO_MEMORIZED_SIMPLE_VOLUME,
        name="Memorized simple volume",
        options=[OverkizCommandParam.STANDARD, OverkizCommandParam.HIGHEST],
        select_option=_select_option_memorized_simple_volume,
        entity_category=EntityCategory.CONFIG,
        translation_key="memorized_simple_volume",
    ),
    # SomfyHeatingTemperatureInterface
    OverkizSelectDescription(
        key=OverkizState.OVP_HEATING_TEMPERATURE_INTERFACE_OPERATING_MODE,
        name="Operating mode",
        options=[OverkizCommandParam.HEATING, OverkizCommandParam.COOLING],
        select_option=lambda option, execute_command: execute_command(
            OverkizCommand.SET_OPERATING_MODE, option
        ),
        entity_category=EntityCategory.CONFIG,
        translation_key="operating_mode",
    ),
    # StatefulAlarmController
    OverkizSelectDescription(
        key=OverkizState.CORE_ACTIVE_ZONES,
        name="Active zones",
        options=["", "A", "B", "C", "A,B", "B,C", "A,C", "A,B,C"],
        select_option=_select_option_active_zone,
        translation_key="active_zones",
    ),
]

SUPPORTED_STATES = {description.key: description for description in SELECT_DESCRIPTIONS}


async def async_setup_entry(
    hass: HomeAssistant,
    entry: OverkizDataConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the Overkiz select from a config entry."""
    data = entry.runtime_data
    entities: list[OverkizSelect] = []

    for device in data.coordinator.data.values():
        if (
            device.widget in IGNORED_OVERKIZ_DEVICES
            or device.ui_class in IGNORED_OVERKIZ_DEVICES
        ):
            continue

        entities.extend(
            OverkizSelect(
                device.device_url,
                data.coordinator,
                description,
            )
            for state in device.definition.states
            if (description := SUPPORTED_STATES.get(state.qualified_name))
        )

    async_add_entities(entities)


class OverkizSelect(OverkizDescriptiveEntity, SelectEntity):
    """Representation of an Overkiz Select entity."""

    entity_description: OverkizSelectDescription

    @property
    def current_option(self) -> str | None:
        """Return the selected entity option to represent the entity state."""
        if state := self.device.states.get(self.entity_description.key):
            return str(state.value)

        return None

    async def async_select_option(self, option: str) -> None:
        """Change the selected option."""
        await self.entity_description.select_option(
            option, self.executor.async_execute_command
        )
